#!/usr/bin/env python
#
# Copyright (C) 2011 Clement Lorteau <clem@lorteau.fr>	
#		
# This file is part of QuickScreenShots.		
# 	
# This program is free software; you can redistribute it and/or modify	
# it under the terms of the GNU General Public License as published by		
# the Free Software Foundation; version 3.	
# 	
# This program is distributed in the hope that it will be useful,		
# but WITHOUT ANY WARRANTY; without even the implied warranty of		
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the		
# GNU General Public License for more details.		
# 		
# You should have received a copy of the GNU General Public License		
# along with this program; if not, write to the Free Software		
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

import wx
import os
import sys
import time
import platform
import systray
import options
import mainframe
import updates
import optionsdialog
import fullscreenshot
import windowscreenshot
if platform.system() == 'Windows':
	import regionscreenshot
	import _winreg as winreg
import dropshadow
import monochrome
import rotate
import checkyesnodialog
import string
import re
import webbrowser
import pyexiv2
from options import Options, Option
from tempfile import NamedTemporaryFile
from __init__ import resources, __version__

class QSSApp(wx.App):
    def __init__(self, *args, **kwds):
        self.__load_options()
        wx.App.__init__(self, *args, **kwds)
        wx.InitAllImageHandlers()
        self.__check_updates()
        self.__load_sound()
        self.__load_optionsdialog()
        self.__load_systray()
        self.__load_mainframe()
        
        self.Bind(systray.EVT_SYSTRAY_EVENT, self.OnSystray)
        self.Bind(mainframe.EVT_COMMAND_EVENT, self.OnCommand)
        self.Bind(mainframe.EVT_SCREENSHOT_EVENT, self.OnScreenshot)
        self.Bind(mainframe.EVT_EFFECT_EVENT, self.OnEffect)
        self.Bind(mainframe.EVT_DRAWING_EVENT, self.OnDrawing)
        
    def __check_updates(self):
        """Function to check for updates on startup"""
        try:
            ret = updates.check_updates()
        except Exception, e:
            msg = "Error when checking for updates: %s" % str(e)
            print msg
            return
        if ret:
            title = "QuickScreenShots: new update available"
            message = "A new version of QuickScreenShots is available.\nDo you want to visit the download page?"
            check_text = "Don't ask me again"
            dlg = checkyesnodialog.CheckYesNoDialog(title, message, check_text, None,-1, "")
            ret = dlg.ShowModal()
            if dlg.GetChecked():
                self.options.check_updates.SetValue(False)
            if ret == wx.ID_OK:
                webbrowser.open(updates.download_page)
            dlg.Destroy()
        
    def __load_sound(self):
        self.snapsound = wx.Sound("%s/snap.wav" % resources)
        
    def __reset_options(self):
        dlg = wx.MessageDialog(None,
                               'Your config file seems incompatible with this version of QuickScreenShots. Reseting it.',
                               caption='Resetting options',
                               style=wx.OK|wx.ICON_EXCLAMATION)
        dlg.ShowModal()
        dlg.Destroy()
        self.options = Options()
        self.optionsdialog = optionsdialog.OptionsDialog(self.options, None, -1, "")
        
    def __load_optionsdialog(self):
        try:
            self.optionsdialog = optionsdialog.OptionsDialog(self.options, None, -1, "")
        except Exception, e:
            print "Error while loading options: %s" % str(e)
            self.__reset_options()
        is_frozen = hasattr(sys, "frozen")
        if not is_frozen:
            self.optionsdialog.autostartcheckbox.Enable(False)
    
    def __load_options(self):
        try:
            opts = options.load()
        except IOError, e:
            opts = Options()
            if e.errno == 2:
                pass # the file or directory didn't exist; it was created
            else:
                print "Error while loading options: %s" % str(e)
        self.options = opts
    
    def __load_systray(self):
        self.tray = systray.QSSsystray(self.options, self)
        
    def __load_mainframe(self):
        try:
            self.mainframe = mainframe.QSSFrame(self.options, self, None, -1, "")
        except AttributeError, e:
            print "Error while loading options: %s" % str(e)
            self.__reset_options()
            self.mainframe = mainframe.QSSFrame(self.options, self, None, -1, "")
            
        if not self.options.start_minimized.GetValue():
            self.mainframe.Show()
            
    def MacReopenApp(self):
    	if self.mainframe.IsShown():
    		self.mainframe.Raise()
    	else:
    		self.Restore()
            
    def CheckUpdate(self):
        ret = False
        try:
            ret = updates.check_updates(now=True)
        except Exception, e:
            msg = "Error when trying to look for updates: %s" % str(e)
            dlg = wx.MessageDialog(self.mainframe, msg, caption='Check for updates failed',
                                   style=wx.OK|wx.ICON_EXCLAMATION)
            dlg.ShowModal()
            dlg.Destroy()
            return
        if ret:
            title = "QuickScreenShots: new update available"
            message = "A new version of QuickScreenShots is available.\nDo you want to visit the download page?"
            check_text = ''
            dlg = checkyesnodialog.CheckYesNoDialog(title, message, check_text, None,-1, "")
            ret = dlg.ShowModal()
            if ret == wx.ID_OK:
                webbrowser.open(updates.download_page)
            dlg.Destroy()
        else:
            dlg = wx.MessageDialog(self.mainframe, 'You already use the latest version.',
                                   caption='No new update', style=wx.OK|wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()
            
    def GetNextFilename(self):
        """Get the full file name of the next auto saved screenshot"""
        save_dir = self.options.save_dir.GetValue()
        base_name = self.options.save_file.GetValue()
        allfiles = None
        try:
            allfiles = os.listdir(save_dir)
        except WindowsError, e:
            if e.winerror == 3: #windows error ERROR_PATH_NOT_FOUND
                os.makedirs(save_dir)
                allfiles = os.listdir(save_dir)
            else:
                raise e
        ext = 'jpg'
        currentfiles = []
        for f in allfiles:
            if f.startswith(base_name):
                currentfiles.append(f)
        currentfiles.sort()
        if len(currentfiles) == 0:
            nextfile = save_dir+'\\'+base_name+'001.'+ext
        else:
            lastfile = currentfiles[-1:][0]
            regexp = base_name+'(\d{3})\.'+ext
            m = re.search(regexp, lastfile)
            if m:
                d = int(m.group(1))
            else:
                d = 0
            nextfile = "%s\\%s%03d.%s" % (save_dir, base_name, d+1, ext)
        return nextfile
            
    def OnRegionSnap(self, event):
        im = self.magnifierFrame.screenshotim
        self.magnifierFrame.Destroy()
        self.ProcessScreenshot(im)
        self.mainframe.Show(self.show)
        self.mainframe.Raise()
        
    def OnRegionEsc(self, event):
        self.magnifierFrame.Destroy()
        self.mainframe.Show(self.show)
        self.mainframe.Raise()
    
    def OnScreenshot(self, event):
        if event.GetId() == mainframe.ID_DESKTOP:
            self.Screenshot('desktop')
        if event.GetId() == mainframe.ID_WINDOW:
            self.Screenshot('window')
        if event.GetId() == mainframe.ID_REGION:
            self.Screenshot('region')
            
    def OnEffect(self, event):
        newim = None
        if event.GetId() == mainframe.ID_REAPPLY and self.mainframe.GetOrigIM():
            newim = self.mainframe.GetOrigIM()
            newim = self.ApplyEffects(newim)
        if newim:
            self.mainframe.SetIM(newim)
            
    def OnCommand(self, event):
        if event.GetId() == mainframe.ID_COPY:
            self.Copy(event.GetImage())
        if event.GetId() == mainframe.ID_CLOSE:
            self.mainframe.Show(False)
        if event.GetId() == mainframe.ID_VIEW:
            self.View(event.GetImage())
        if event.GetId() == mainframe.ID_PRINT:
            self.Print(event.GetImage())
        if event.GetId() == mainframe.ID_UPDATE:
            self.CheckUpdate()
        if event.GetId() == mainframe.ID_QUIT:
            self.Exit()
        if event.GetId() == mainframe.ID_SAVE:
            self.Save(event.GetImage(), event.GetParams())
        if event.GetId() == mainframe.ID_PREFS:
            self.ShowPrefs(event.GetParams())
            
    def OnDrawing(self, event):
        if self.options.copy_use.GetValue():
            self.Copy(self.mainframe.GetIM())    
        
    def OnSystray(self, event):
        if event.GetId() == systray.ID_QUIT:
            self.Exit()
        if event.GetId() == systray.ID_RESTORE:
            self.Restore()
        if event.GetId() == systray.ID_SETTINGS:
            self.ShowPrefs('general')
        if event.GetId() == systray.ID_DESKTOP:
            self.Screenshot('desktop')
        if event.GetId() == systray.ID_WINDOW:
            self.Screenshot('window')
        if event.GetId() == systray.ID_REGION:
            self.Screenshot('region')
            
    def Copy(self, image):
        clip = wx.Clipboard.Get()
        clip.Open()
        data = wx.BitmapDataObject(self.mainframe.BitmapFromImage(image))
        clip.SetData(data)
        clip.Close()
        
    def Print(self, image):
        pass
        
    def View(self, image):
        """Open the image with user's default picture viewer"""
        tempfile = NamedTemporaryFile(prefix='qs_', delete=False)
        tempfile.close()
        self.Save(image, tempfile.name+'.jpg')
        os.startfile(tempfile.name+'.jpg', 'open')
        
    def Save(self, image, filename):
        """Save image to file, then writes EXIF to that file"""
        image.save(filename)
        exif = {'Exif.Image.Software': 'QuickScreenShots ' + __version__,
                'Exif.Image.ImageDescription': 'Screenshot'
               }
        metadata = pyexiv2.ImageMetadata(filename)
        metadata.read()
        for key, value in exif.iteritems():
            metadata[key] = value
        metadata.write()
        
    def Shadow(self, image, color, background):
        newim = dropshadow.dropShadow(image, background=background, shadow=color)
        return newim
            
    def ShowPrefs(self, tab_name):
        if tab_name == 'general':
            self.optionsdialog.book.SetSelection(0)
        if tab_name == 'save':
            self.optionsdialog.book.SetSelection(1)
        if tab_name == 'shadow':
            self.optionsdialog.book.SetSelection(2)
        if tab_name == 'rotate':
            self.optionsdialog.book.SetSelection(3)
        ret = self.optionsdialog.ShowModal()
        if ret == wx.ID_OK:
            kb_desktop = self.options.kb_desktop.GetValue()
            kb_window = self.options.kb_window.GetValue()
            kb_region = self.options.kb_region.GetValue()
            o = self.optionsdialog.GetOptions()
            self.SetAutoStart(o.win_start.GetValue())
            if kb_desktop != o.kb_desktop.GetValue() or \
               kb_window != o.kb_window.GetValue() or \
               kb_region != o.kb_region.GetValue():
                dlg = wx.MessageDialog(self.mainframe,
                                       'Changes to keyboard shortcuts will be applied only after the application is restarted.',
                                       caption='Application restart required',
                                       style=wx.OK|wx.ICON_EXCLAMATION)
                dlg.ShowModal()
            self.options = o
            self.mainframe.SetOptions(self.options)
        self.optionsdialog.Hide()
        
    def SetAutoStart(self, autostart):
        # TODO: OS X implementation
        is_frozen = hasattr(sys, "frozen")
        if not is_frozen:
            return
        module_path = os.path.dirname(unicode(sys.executable, sys.getfilesystemencoding( )))
        exe_name = sys.argv[0].split('\\')[-1:][0]
        exe_path = "\"%s\\%s\"" % (module_path, exe_name)
        subkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, subkey, 0, winreg.KEY_SET_VALUE)
        if autostart:
            winreg.SetValueEx(key, 'QuickScreenShots', 0, winreg.REG_SZ, exe_path)
        else:
            winreg.DeleteValue(key, 'QuickScreenShots')
            
    def Exit(self):
        self.options.Save()
        wx.App.Exit(self)
        
    def Restore(self):
        self.mainframe.Show(not self.mainframe.IsShown())
        if self.mainframe.IsShown():
            self.mainframe.Raise()
        
    def Screenshot(self, type):
        """Actually perform the desired screenshot"""
    
        #in case we're in drawing mode, unpress the drawing buttons
        for b in self.mainframe.drawingtoolsbuttons:
            b.SetValue(False)
            
        self.show = self.mainframe.IsShown()
        self.mainframe.Show(False)
        wx.Yield()
        if self.options.delay.GetValue() == 0:
            time.sleep(0.1) #give time to the main dialog to hide
        else:
            time.sleep(self.options.delay.GetValue())
        include_cursor = self.options.include_cursor.GetValue()
        if type == 'desktop':
            (im, bmp) = fullscreenshot.snap(include_cursor)
        if type == 'window':
            (im, bmp) = windowscreenshot.snap(include_cursor)
        if type == 'region':
            self.magnifierFrame = regionscreenshot.magnifierFrame(self, include_cursor, None, -1, '')
            self.magnifierFrame.Show()
            self.Bind(self.magnifierFrame.EVT_SNAP_EVENT, self.OnRegionSnap)
            self.Bind(self.magnifierFrame.EVT_ESC_EVENT, self.OnRegionEsc)
            return
        self.ProcessScreenshot(im)
        self.mainframe.Show(self.show)
        
    def ApplyEffects(self, im):
        if self.options.shadow_use.GetValue():
            im = self.Shadow(im,
                             self.options.shadow_color.GetValue(),
                             self.options.shadow_background.GetValue())
        if self.options.rotate_use.GetValue():
            im = rotate.rotate(im,
                               self.options.rotate_angle.GetValue(),
                               self.options.rotate_background.GetValue())
        if self.options.mono_use.GetValue():
            im = monochrome.mono(im)
        return im
        
    def ProcessScreenshot(self, im):
        """Apply configured effects and commands to the image resulting
           from a screenshot"""
        self.mainframe.SetOrigIM(im.copy())
        im = self.ApplyEffects(im)
        if self.options.sound_use.GetValue():
            self.snapsound.Play()
        if self.options.copy_use.GetValue():
            self.Copy(im)
        if self.options.save_use.GetValue():
            self.Save(im, self.GetNextFilename())
        self.mainframe.SetIM(im)
        
    def OnExit(self):
        self.tray.RemoveIcon()
                
if __name__ == '__main__':
    wx.InitAllImageHandlers()
    app = QSSApp(redirect=False)
    # import wx.lib.inspection
    # wx.lib.inspection.InspectionTool().Show()
    app.MainLoop()